package org.example.securitydemo.config;

import com.alibaba.fastjson2.JSON;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

import java.util.HashMap;
import java.util.Map;

import static org.springframework.security.config.Customizer.withDefaults;

// https://docs.spring.io/spring-security/reference/servlet/configuration/java.html
// 查看官网配置
// @EnableWebSecurity // 开启SpringSecurity的自定义配置功能(在springboot项目中可以省略此注解)，因为在pom文件中已经加载了spring-boot-starter-security
@Configuration // 配置类
@EnableMethodSecurity // 开启基于方法的授权,这个开启了以后，下面的.requestMatchers这个就可以不需要了，另外开启这个后，所有的方法都有权限了，在方法上面开启@preauthorize注解
public class WebSecurityConfig {
    // @Bean
    // public UserDetailsService userDetailsService() {
    //     // 创建基于内存的用户信息管理器
    //     InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    //     // 使用manager管理UserDetails对象
    //     manager.createUser(
    //         // 创建UserDetails对象,用于管理用户名、用户密码、用户角色、用户权限等内容
    //         User.withDefaultPasswordEncoder().username("user").password("password").roles("USER").build()
    //     );
    //     return manager;
    // }

    // 也可以把当前这个类的注解去掉，直接在DBUserDetailsManager设置
    // @Bean
    // public UserDetailsService userDetailsService() {
    //     // 创建基于数据库的用户信息管理器
    //     return new DBUserDetailsManager();
    // }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // 开启授权保护
        http.authorizeHttpRequests(
                authorize -> authorize
                // 具有USER_LIST权限的用户可以访问/user/list
                // .requestMatchers("/user/list").hasAuthority("USER_LIST")
                // 具有USER_ADD权限的用户可以访问/user/add
                // .requestMatchers("/user/add").hasAuthority("USER_ADD")
                // 基于角色的授权，配置用户角色，只有ADMIN角色的用户可以访问/user/**
                // .requestMatchers("/user/**").hasRole("ADMIN")

                // 对所有请求开启授权保护
                .anyRequest()
                // 已认证的请求会被自动授权
                .authenticated()
        );

        // 使用自己定义的登陆页面,.permitAll()无需授权即可访问当前页面
        http.formLogin(form->form
                .loginPage("/login").permitAll() // .permitAll()无需授权即可访问当前页面
                .usernameParameter("myusername") // 配置自定义的表单用户名参数 默认值：username
                .passwordParameter("mypassword") // 配置自定义的表单密码参数 默认值：password
                .failureUrl("/login?error") // 校验失败时跳转的地址，默认值时error

                .successHandler(new MyAuthenticationSuccessHandler()) // 认证成功时的处理
                .failureHandler(new MyAuthenticationFailureHandler()) // 认证失败时的处理
        );

        // 配置注销
        http.logout(logout->
                logout
                // .logoutUrl("/logout") // 配置注销地址
                .logoutSuccessHandler(new MyLogoutSuccessHandler()) // 配置注销成功后的处理
                // .permitAll()// .permitAll()无需授权即可访问当前页面
        );
        // .formLogin(Customizer.withDefaults()); // 使用表单授权方式
        // .httpBasic(Customizer.withDefaults());// 使用基本授权方式，浏览器自带的登录方式


        http.exceptionHandling(
                exception->{
                    exception.authenticationEntryPoint(new MyAuthenticationEntryPoint());// 请求未认证的接口处理
                    // exception.accessDeniedHandler(new MyAccessDeniedHandler());// 请求未授权的接口处理
                    exception.accessDeniedHandler((request, response, accessDeniedException)->{
                        Map<String,Object> result = new HashMap<>();
                        result.put("code", -1); // 失败
                        result.put("message", "没有权限");

                        // 将结果对象转换成json字符串
                        String json = JSON.toJSONString(result);

                        // 返回json数据到前端
                        response.setContentType("application/json;charset=UTF-8");
                        response.getWriter().write(json);
                    });// 请求未授权的接口处理
                }
        );

        // 处理跨域
        http.cors(withDefaults());

        // 最多只允许一个账号登陆，后面登陆的会把前面登陆的信息踢掉
        http.sessionManagement(session->session.maximumSessions(1)
                .expiredSessionStrategy(new MySessionInformationExpiredStrategy()));

        // 关闭csrf攻击防御
        http.csrf(AbstractHttpConfigurer::disable);// 暂时先关系csrf,因为在接口那边现在无法测试表单生成的_csrf
        // http.csrf(csrf->csrf.disable());// 暂时先关系csrf,因为在接口那边现在无法测试表单生成的_csrf
        return http.build();
    }
}
